/*-
 * Useful structures and methods for the generator.
 *
 * Copyright (c) 2010 Alex Suhan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer
 * in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.io.PrintStream;

class CgenConstants
{
    static final int INVALID_TAG = -1;
    static final int OBJECT_TAG = 0;
    static final int INTEGER_TAG = 1;
    static final int BOOLEAN_TAG = 2;
    static final int STRING_TAG = 3;
    static final int CONSOLE_TAG = 4;
    static final String strConstPrefix = "str_const";
    static final String intConstPrefix = "int_const";
    static final String boolConstPrefix = "bool_const";
    
    static final int DEFAULT_OBJFIELDS = 3;
    static final int STRING_SLOTS = 1;
    static final int INT_SLOTS = 1;
    static final int BOOL_SLOTS = 1;
}

class IRLingo
{
    static final String defineWord = "DW";
    static final String defineLabel = "DL";
    static final String defineStorage = "DS";
    static final String defineBytes = "DB";
    
    static final String dispatchSuffix = "_dispatch";
    static final String protoSuffix = "_protObj";
    static final String initSuffix = "_init";
    static final String classNameTable = "class_nameTab";
    static final String classObjInfoTable = "class_objTab";
    static final String methodTag = ".function";
    
    static String void_disp_handler = "void_disp_handler";
}

/**
 * A word or two is needed about the code generation strategy. It works
 * almost like a stack machine with a static stack growing from VR0, VR1, VR2
 * to infinity. Pushing the register stack means incrementing the index of
 * the next available register, therefore protecting the current value from
 * the "top" (current register). Popping the stack means exactly the opposite.
 * These two actions don't actually emit code, so the 'emitPushRegStack' /
 * 'emitPopRegStack' are slightly misleading; they simply update the current
 * register index.
 */
class CgenSupport
{
    static final String OBJCOPY = "Object.copy";
    static final String ite_end = "ite_end";
    static final String ite_false = "ite_false";
    static final String isvoid_end = "isvoid_end";
    static final String isvoid_true = "isvoid_true";
    static final String loop_start = "loop_start";
    static final String loop_end = "loop_end";
    static final String _dispatch_abort = "_dispatch_abort";
    static final String dispatch_notvoid = "dispatch_notvoid";
    static final String EQ_TST_LABEL = "equality_test";
    static final int firstIRReg = 1;
    static final String IR_SELF = "VR0";
    static final int WORD_SIZE = 4;
    static final String LOAD = "\tload\t";
    static final String STORE = "\tstore\t";
    static final String JUMPF = "\tjumpf\t";
    static final String JUMPT = "\tjumpt\t";
    static final String JUMP = "\tjump\t";
    static final String CALL = "\tcall\t";
    static final String VR = "VR";
    
    static void emitPushRegStack(int count, CodeSection ctx) {
        ctx.crtRegIdx += count;
    }

    static void emitPopRegStack(int count, CodeSection ctx) {
        ctx.crtRegIdx -= count;
    }

    static void emitIRLoad(String dest_reg, int offset, String source_reg, PrintStream s) {
        s.println(LOAD + dest_reg + " " + "[" + source_reg + ", " + offset * WORD_SIZE + "]");
    }

    static void emitIRStore(String source_reg, int offset, String dest_reg, PrintStream s) {
        s.println(STORE + source_reg + " " + "[" + dest_reg + ", " + offset * WORD_SIZE + "]");
    }

    static void emitIRLoadAddress(String dest_reg, String address, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + address);
    }

    static void emitIRAdd(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " + " + src2);
    }

    static void emitIRDiv(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " / " + src2);
    }

    static void emitIRMul(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " * " + src2);
    }

    static void emitIRSub(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " - " + src2);
    }
    
    static void emitIRLt(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " < " + src2);
    }
    
    static void emitIRLeq(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " <= " + src2);
    }
    
    static void emitIREq(String dest_reg, String src1, String src2, PrintStream s) {
        s.println("\t" + dest_reg + "\t<-\t" + src1 + " = " + src2);
    }

    static void emitIRJumpf(String src, String label, PrintStream s) {
        s.println(JUMPF + src + " " + label);
    }

    static void emitIRJumpt(String src, String label, PrintStream s) {
        s.println(JUMPT + src + " " + label);
    }

    static void emitIRJump(String label, PrintStream s) {
        s.println(JUMP + label);
    }
    
    static void emitIRCall(String callAddr, PrintStream s) {
        s.println(CALL + callAddr);
    }
    
    static void emitIRMov(String dst, String src, PrintStream s) {
        s.println("\t" + dst + "\t<-\t" + src);
    }

    static String VR(int index) {
        return VR + (index + firstIRReg);
    }

    static void emitBoxInt(String srcReg, String dstReg, PrintStream s) {
        s.println("\t# boxing integer from " + srcReg + " to " + dstReg);
        CgenSupport.emitIRMov(dstReg, ScalaConstants.IntegerClassname + IRLingo.protoSuffix, s);
        CgenSupport.emitIRMov("VI0", dstReg, s);
        emitIRCall(OBJCOPY, s);
        CgenSupport.emitIRMov(dstReg, "VI0", s);
        emitIRStore(srcReg, 3, dstReg, s);
        s.println("\t# end boxing");
    }

    static void emitUnboxInt(String srcReg, String dstReg, PrintStream s) {
        s.println("\t# unboxing integer from " + srcReg + " to " + dstReg);
        CgenSupport.emitIRLoad(dstReg, 3, srcReg, s);
    }
    
    static void emitBoxBool(String srcReg, String dstReg, PrintStream s) {
        s.println("\t# boxing boolean from " + srcReg + " to " + dstReg);
        CgenSupport.emitIRMov(dstReg, ScalaConstants.BooleanClassname + IRLingo.protoSuffix, s);
        CgenSupport.emitIRMov("VI0", dstReg, s);
        emitIRCall(OBJCOPY, s);
        CgenSupport.emitIRMov(dstReg, "VI0", s);
        emitIRStore(srcReg, 3, dstReg, s);
        s.println("\t# end boxing");
    }
    
    static void emitUnboxBool(String srcReg, String dstReg, PrintStream s) {
        s.println("\t# unboxing boolean from " + srcReg + " to " + dstReg);
        CgenSupport.emitIRLoad(dstReg, 3, srcReg, s);
    }
}

// TODO: add getters and setters
class ScalaClassLayout
{
    // attributes and methods, including the inherited ones
    Map<String, Integer> attrOffset;
    Map<String, CgenMethodInfo> methodOffset;
    ScalaClassLayout parent;
    List<ScalaClassLayout> children;
    int tag;
    int nextAttrOffset;
    int nextMethodOffset;
    String name;
    
    ScalaClassLayout(String name)
    {
        attrOffset = new HashMap<String, Integer>();
        methodOffset = new HashMap<String, CgenMethodInfo>();
        children = new LinkedList<ScalaClassLayout>();
        tag = CgenConstants.INVALID_TAG;
        this.name = name;
    }
    
    void setParent(ScalaClassLayout parent)
    {
        this.parent = parent;
    }
    
    void addChild(ScalaClassLayout child)
    {
        children.add(child);
    }
    
    List<ScalaClassLayout> getChildren()
    {
        return children;
    }
    
    @Override
    public String toString()
    {
        return name + " " + attrOffset + " " + methodOffset + " " + tag;
    }
    
    static class CgenMethodInfo
    {
        int offset;
        String className;
        Map<String, Integer> localVarOffset;
        
        CgenMethodInfo(int offset, String className)
        {
            this.offset = offset;
            this.className = className;
            localVarOffset = new HashMap<String, Integer>();
        }
        
        void putLocalVar(String name, int offset)
        {
            localVarOffset.put(name, offset);
        }
        
        int getLocalVarOffset(String name)
        {
            return localVarOffset.get(name);
        }
    }
    
    static class CgenAttributeInfo
    {
        int offset;
        String name;
        
        CgenAttributeInfo(String name, int offset)
        {
            this.offset = offset;
            this.name = name;
        }
    }
}
